#pragma once
#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Watcher.hpp>
#include <etcd/Value.hpp>
#include <functional>
#include "logger.hpp"

// 二次封装
// 目的: 更易于使用, 封装成自己熟悉的样子

namespace wufan_im{
//服务注册客户端类
//  提供一个接口: 向服务器新增数据时, 已经填好了保活参数      参数1.注册中心地址(etcd服务器地址), 新增的服务信息(服务名 - 主机地址)
class Registry {
    public:
        using ptr = std::shared_ptr<Registry>;
        Registry(const std::string& host)
        :_client(new etcd::Client(host))
        ,_keep_alive(_client->leasekeepalive(3).get())
        ,_lease_id(_keep_alive->Lease())
        {}
        ~Registry(){ _keep_alive->Cancel(); }   //这个_keep_alive析构的时候, 会自动的cancel, 我们这里写不写都行
        bool registry(const std::string& key, const std::string& val){
            auto resp = _client->put(key, val, _lease_id).get();
            if (resp.is_ok() == false) {
                LOG_ERROR("注册数据失败: {}", resp.error_message());
                return false;
            }
            return true;
        }
    private:
        std::shared_ptr<etcd::Client> _client;  //将这些设置为智能指针, 主要就是可以延迟初始化, 提高性能
        //为什么要有client, 因为client是客户端主体类

        //其实这个完全可以定义在函数内部, 但是可能第二个函数里, 需要这个变量. 为什么有成员函数, 就是因为先有局部函数才有成员函数
        std::shared_ptr<etcd::KeepAlive> _keep_alive;   
        //为什么要有保活对象, 因为需要保活功能
        uint64_t _lease_id;
};


//服务发现客户端
//  提供两个回调函数 1.服务上线事件该怎么处理           2.服务下线事件该怎么处理    - 本来watch的callback只能设置一个回调函数, 而我们改为了能设置两个回调函数
//  提供一个设置根目录的接口: 用于获取制定目录下的数据以及目录下数据的改变
class Discovery{
    public:
        using ptr = std::shared_ptr<Discovery>;
        using NotifyCallback = std::function<void(std::string, std::string)>;   //哪个服务(string)上线了, 主机地址(string)
        //封装的意思就是, 哪个客户端监控哪个key, put服务上线后该怎么办, del服务下线后该怎么办
        Discovery(const std::string& host, const string& basedir, 
                  const NotifyCallback& put_cb, const NotifyCallback& del_cb)
        :_client(new etcd::Client(host))
        ,_put_cb(put_cb), _del_cb(del_cb)
        {
            //先进行服务发现, 先获取到当前已有的数据
            auto resp = _client->ls(basedir).get();
            if (resp.is_ok() == false) {
                LOG_ERROR("获取服务信息数据失败: {}", resp.error_message());
                return;
            }
            int sz = resp.keys().size();
            for (int i = 0; i < sz; ++i) {
                LOG_DEBUG("{}可以提供{}服务", resp.value(i).as_string(), resp.key(i));
                if (_put_cb)
                    _put_cb(resp.key(i), resp.value(i).as_string());        //先处理一遍, 再进行监控
            }
            //然后进行事件监控, 监控数据发生的改变并调用回调进行处理
            _watcher = make_shared<etcd::Watcher>(*_client, basedir, 
                    std::bind(&Discovery::callback, this, std::placeholders::_1), true);
        }
    private:
        void callback(const etcd::Response& resp){
            if (resp.is_ok() == false) {
                LOG_ERROR("收到一个错误的事件通知: {}", resp.error_message());
                return;
            }
            for (const auto& ev : resp.events()) {
                if (ev.event_type() == etcd::Event::EventType::PUT) {
                    LOG_DEBUG("服务信息发生了改变:\n当前的值: {}-{}\n原来的值: {}-{}服务", ev.kv().key(), ev.kv().as_string(), \
                    ev.prev_kv().key(), ev.prev_kv().as_string());
                    if (_put_cb)
                        _put_cb(ev.kv().key(), ev.kv().as_string());
                } else if (ev.event_type() == etcd::Event::EventType::DELETE_) {
                    LOG_DEBUG("服务信息下线被删除:\n当前的值: {}-{}\n原来的值: {}-{}服务", ev.kv().key(), ev.kv().as_string(), \
                    ev.prev_kv().key(), ev.prev_kv().as_string());
                    if (_del_cb)
                        _del_cb(ev.kv().key(), ev.kv().as_string());
                }
            }
        }
    private:
        NotifyCallback _put_cb;
        NotifyCallback _del_cb;
        std::shared_ptr<etcd::Client> _client;
        std::shared_ptr<etcd::Watcher> _watcher;    //需要监控这个功能, 所以有这个成员
};
}



// #pragma once
// #include <etcd/Client.hpp>
// #include <etcd/KeepAlive.hpp>
// #include <etcd/Response.hpp>
// #include <etcd/Watcher.hpp>
// #include <etcd/Value.hpp>
// #include <functional>
// #include "logger.hpp"

// namespace wufan_im{
// //服务注册客户端类
// class Registry {
//     public:
//         using ptr = std::shared_ptr<Registry>;
//         Registry(const std::string &host):
//             _client(std::make_shared<etcd::Client>(host)) ,
//             _keep_alive(_client->leasekeepalive(3).get()),
//             _lease_id(_keep_alive->Lease()){}
//         ~Registry() { _keep_alive->Cancel(); }
//         bool registry(const std::string &key, const std::string &val) {
//             auto resp = _client->put(key, val, _lease_id).get();
//             if (resp.is_ok() == false) {
//                 LOG_ERROR("注册数据失败：{}", resp.error_message());
//                 return false;
//             }
//             return true;
//         }
//     private:
//         std::shared_ptr<etcd::Client> _client;
//         std::shared_ptr<etcd::KeepAlive> _keep_alive;
//         uint64_t _lease_id;
// };

// //服务发现客户端类
// class Discovery {
//     public:
//         using ptr = std::shared_ptr<Discovery>;
//         using NotifyCallback = std::function<void(std::string, std::string)>;
//         Discovery(const std::string &host, 
//             const std::string &basedir,
//             const NotifyCallback &put_cb,
//             const NotifyCallback &del_cb):
//             _client(std::make_shared<etcd::Client>(host)) ,
//             _put_cb(put_cb), _del_cb(del_cb){
//             //先进行服务发现,先获取到当前已有的数据
//             auto resp = _client->ls(basedir).get();
//             if (resp.is_ok() == false) {
//                 LOG_ERROR("获取服务信息数据失败：{}", resp.error_message());
//             }
//             int sz = resp.keys().size();
//             for (int i = 0; i < sz; ++i) {
//                 if (_put_cb) _put_cb(resp.key(i), resp.value(i).as_string());
//             }
//             //然后进行事件监控，监控数据发生的改变并调用回调进行处理
//             _watcher = std::make_shared<etcd::Watcher>(*_client.get(), basedir,
//                 std::bind(&Discovery::callback, this, std::placeholders::_1), true);
//         }
//         ~Discovery() {
//             _watcher->Cancel();
//         }
//     private:
//         void callback(const etcd::Response &resp) {
//             if (resp.is_ok() == false) {
//                 LOG_ERROR("收到一个错误的事件通知: {}", resp.error_message());
//                 return;
//             }
//             for (auto const& ev : resp.events()) {
//                 if (ev.event_type() == etcd::Event::EventType::PUT) {
//                     if (_put_cb) _put_cb(ev.kv().key(), ev.kv().as_string());
//                     LOG_DEBUG("新增服务：{}-{}", ev.kv().key(), ev.kv().as_string());
//                 }else if (ev.event_type() == etcd::Event::EventType::DELETE_) {
//                     if (_del_cb) _del_cb(ev.prev_kv().key(), ev.prev_kv().as_string());
//                     LOG_DEBUG("下线服务：{}-{}", ev.prev_kv().key(), ev.prev_kv().as_string());
//                 }
//             }
//         }
//     private:
//         NotifyCallback _put_cb;
//         NotifyCallback _del_cb;
//         std::shared_ptr<etcd::Client> _client;
//         std::shared_ptr<etcd::Watcher> _watcher;
// };
// }